﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Unity;
using TracyGUI.View;
using SharedLib.WinFormMVCFramework;
using TracyGUI.View.UIComponent;
using Tracy.DataModel;
using Tracy;
using SharedLib;
using TracyGUI.Model;
using System.Xml;
using System.Windows.Forms;
using SharedLib.Helpers;

namespace TracyGUI.Controller
{
    class UltraExplorerViewController : ControllerBase<IUltraExplorerView>, IController<IUltraExplorerView>, IHasViewState
    {
        protected override IUltraExplorerView ResolveView()
        {
            return Tracy.TracyFacade.Instance.DIContainer.Resolve<IUltraExplorerView>();
        }

        UltraExplorer<Entry> _ueEntry;
        private TreeNode _currentSelectedTreeNode = null;
        private string _currentSelectedClassifierSet = null;
        public UltraExplorerViewController()
        {
            View.ClassiferSetSelected += new EventHandler<GenericEventArgs<string>>(View_ClassiferSetSelected);
            View.ExpandNode += new EventHandler<GenericEventArgs<System.Windows.Forms.TreeNode>>(View_ExpandNode);
            View.NodeSelected += new EventHandler<GenericEventArgs<System.Windows.Forms.TreeNode>>(View_NodeSelected);

            Tracy.TracyFacade.Instance.TagManager.TagAssigned += new EventHandler<EnhancedEventArgs>(TagManager_TagAssigned);
            Tracy.TracyFacade.Instance.TagManager.TagRemoved += new EventHandler<EnhancedEventArgs>(TagManager_TagRemoved);
            Tracy.TracyFacade.Instance.TagManager.TagUpdated += new EventHandler<EnhancedEventArgs>(TagManager_TagUpdated);

            Tracy.TracyFacade.Instance.MediaFileProvider.ItemsAdded += new EventHandler<GenericEventArgs<IList<MediaFile>>>(MediaFileProvider_ItemsAdded);
            Tracy.TracyFacade.Instance.MediaFileProvider.ItemsChanged += new EventHandler<GenericEventArgs<IList<DataChangeSet<MediaFile>>>>(MediaFileProvider_ItemsChanged);
            Tracy.TracyFacade.Instance.MediaFileProvider.ItemsRemoved += new EventHandler<GenericEventArgs<IList<MediaFile>>>(MediaFileProvider_ItemsRemoved);

            Tracy.TracyFacade.Instance.EntryProvider.ItemsAdded += new EventHandler<GenericEventArgs<IList<Entry>>>(EntryProvider_ItemsAdded);
            Tracy.TracyFacade.Instance.EntryProvider.ItemsRemoved += new EventHandler<GenericEventArgs<IList<Entry>>>(EntryProvider_ItemsRemoved);
            Tracy.TracyFacade.Instance.EntryProvider.ItemsChanged += new EventHandler<GenericEventArgs<IList<DataChangeSet<Entry>>>>(EntryProvider_ItemsChanged);
        }

        #region Model Events
        void EntryProvider_ItemsAdded(object sender, GenericEventArgs<IList<Entry>> e)
        {
            foreach (Entry entry in e.Data) _ueEntry.UpdateTreeForItemChange(entry);
        }
        void EntryProvider_ItemsRemoved(object sender, GenericEventArgs<IList<Entry>> e)
        {
            foreach (Entry entry in e.Data) _ueEntry.UpdateTreeForItemChange(entry);
        }
        void EntryProvider_ItemsChanged(object sender, GenericEventArgs<IList<DataChangeSet<Entry>>> e)
        {
            UpdateTreeForEntryChangeInList<DataChangeSet<Entry>>(e.Data,
                change => change.OriginalData.TracingDisabled != change.Data.TracingDisabled,
                change => change.Data);
        }

        void UpdateTreeForEntryChangeInList<T>(IList<T> list, Func<T, bool> condition, Func<T, Entry> getEntry)
        {
            List<Entry> entryList = new List<Entry>();
            foreach (T item in list)
            {
                if (condition(item))
                {
                    Entry entry = getEntry(item);
                    if (!entryList.Contains(entry))
                    {
                        entryList.Add(entry);
                    }
                }
            }

            foreach (Entry entry in entryList) _ueEntry.UpdateTreeForItemChange(entry);
        }

        void MediaFileProvider_ItemsAdded(object sender, GenericEventArgs<IList<MediaFile>> e)
        {
            UpdateTreeForEntryChangeInList(e.Data, file => file.Visible == "true", file => file.Resource.Entry);
        }
        void MediaFileProvider_ItemsChanged(object sender, GenericEventArgs<IList<DataChangeSet<MediaFile>>> e)
        {
            UpdateTreeForEntryChangeInList(e.Data,
                change => change.OriginalData.Visible != change.Data.Visible ||
                    CommonHelper.CheckCriticalValueChange<MediaFile>(file => file.Status, "Ready", change.OriginalData, change.Data),
                    change => change.Data.Resource.Entry);

        }
        void MediaFileProvider_ItemsRemoved(object sender, GenericEventArgs<IList<MediaFile>> e)
        {
            UpdateTreeForEntryChangeInList(e.Data, file => file.Visible == "true", file => file.Resource.Entry);
        }

        void TagManager_TagUpdated(object sender, EnhancedEventArgs e)
        {
            _ueEntry.UpdateTreeForItemChange((Entry)e.Attributes["entry"]);
        }

        void TagManager_TagRemoved(object sender, EnhancedEventArgs e)
        {
            _ueEntry.UpdateTreeForItemChange((Entry)e.Attributes["entry"]);
        }

        void TagManager_TagAssigned(object sender, EnhancedEventArgs e)
        {
            _ueEntry.UpdateTreeForItemChange((Entry)e.Attributes["entry"]);
        }
        #endregion

        #region Public Interface
        public event EventHandler<GenericEventArgs<ICollection<Entry>>> EntriesFilteredOut;

        public void Init()
        {
            _ueEntry = new UltraExplorer<Entry>(View.TreeFilter, TracyFacade.Instance.LanguageManager.GetText("UltraExplorerAllEntries"));
            EntriesFilteredOut.InvokeEvent(this, new GenericEventArgs<ICollection<Entry>>(_ueEntry.GetAllItems()));
            View.LoadClassiferSets(TracyFacade.Instance.EntryClassifierProvider.ClassifierSets.Keys);
        }
        #endregion

        #region View Events
        void View_ClassiferSetSelected(object sender, GenericEventArgs<string> e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                _currentSelectedClassifierSet = e.Data;
                if (_ueEntry.IsLastGeneratedNode(_ueEntry.RootNode)) return;

                _ueEntry.CurrentClassifierSet = TracyFacade.Instance.EntryClassifierProvider.ClassifierSets[e.Data];
                _ueEntry.ReloadTree();
            }, "UI Policy");
        }

        void View_ExpandNode(object sender, GenericEventArgs<System.Windows.Forms.TreeNode> e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                if (_ueEntry.IsLastGeneratedNode(e.Data))
                {
                    e.Data.Nodes.Clear();
                    _ueEntry.GenerateFilterChildNodes(e.Data);
                }
            }, "UI Policy");
        }

        void View_NodeSelected(object sender, GenericEventArgs<System.Windows.Forms.TreeNode> e)
        {
            Tracy.TracyFacade.Instance.ExceptionManager.Process(() =>
            {
                UENodeSelected(e.Data);
            }, "UI Policy");
        }

        void UENodeSelected(TreeNode node)
        {
            _currentSelectedTreeNode = node;
            EntriesFilteredOut.InvokeEvent(this, new GenericEventArgs<ICollection<Entry>>(_ueEntry.GetMatchedItems(node)));
        }

        #endregion

        public void LoadViewState(ViewStateManager manager, System.Xml.XmlElement viewStateNode)
        {
            string classifierSet = viewStateNode.GetAttribute("classifier-set");
            View.SelectClassifierSet(classifierSet);
            XmlNodeList classiferNodes = viewStateNode.SelectNodes("classifier");
            if (classiferNodes.Count > 0)
            {
                TreeNode parentNode = _ueEntry.TreeView.Nodes[0];
                if (classiferNodes.Count == 1) //Select "All Entries"
                {
                    _ueEntry.TreeView.SelectedNode = parentNode;
                    UENodeSelected(parentNode);//UI will not trigger node selected event, do it directly
                }
                else //Select other node
                {

                    for (int i = 1; i < classiferNodes.Count; i++)
                    {
                        string key = classiferNodes[i].InnerText;
                        foreach (TreeNode subNode in parentNode.Nodes)//expand treeview
                        {
                            if (subNode.Name == key)
                            {
                                subNode.Nodes.Clear();
                                _ueEntry.GenerateFilterChildNodes(subNode);
                                if (i < classiferNodes.Count - 1)
                                {
                                    subNode.Expand();
                                    parentNode = subNode;
                                }
                                else
                                {
                                    _ueEntry.TreeView.SelectedNode = subNode;
                                    UENodeSelected(subNode);//UI will not trigger node selected event, do it directly
                                }

                            }
                        }
                    }
                }
            }
        }

        public void SaveViewState(ViewStateManager manager, System.Xml.XmlElement viewStateNode, System.Xml.XmlDocument doc)
        {
            if (!String.IsNullOrEmpty(_currentSelectedClassifierSet))
            {
                viewStateNode.SetAttribute("classifier-set", _currentSelectedClassifierSet);
                XmlNode classifierNode = null;
                if (_currentSelectedTreeNode != null)
                {
                    TreeNode tn = _currentSelectedTreeNode;
                    do
                    {

                        if (classifierNode != null)
                        {
                            classifierNode = viewStateNode.InsertBefore(doc.CreateElement("classifier"), classifierNode);
                        }
                        else
                        {
                            classifierNode = viewStateNode.AppendChild(doc.CreateElement("classifier"));
                        }

                        classifierNode.InnerText = tn.Name;
                        tn = tn.Parent;
                    } while (tn != null);
                }
            }
            
        }
    }
}
